package com.echovin.esb.gateway.filter;

import com.echovin.esb.gateway.constants.OrderConstants;
import com.echovin.esb.gateway.model.GatewayAccessLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR;

/**
 * 缓存请求体，解决校验参数获取不到body问题
 */
@Slf4j
@Component
public class CacheRequestBodyGlobalFilter implements Ordered, GlobalFilter {

    public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
    public static final String GATEWAY_ACCESS_LOG_KEY = "GatewayAccessLog";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI requestUri = request.getURI();
        GatewayAccessLog accessLog = new GatewayAccessLog();
        exchange.getAttributes().put(GATEWAY_ACCESS_LOG_KEY, accessLog);

        // 只处理 http 请求(包含 https)
        String schema = requestUri.getScheme();
        if (!schema.startsWith("http")) {
            return chain.filter(exchange);
        }
        HttpHeaders headers = request.getHeaders();
        MediaType contentType = headers.getContentType();
        if(headers.getContentLength() <= 0) {
            return chain.filter(exchange);
        }
        // 只处理 application/xml、application/json 格式参数，并且处理webservice
        if (!MediaType.APPLICATION_JSON.equals(contentType) && !MediaType.APPLICATION_XML.equals(contentType)) {
            if (StringUtils.isEmpty(headers.getFirst("SOAPAction"))) {
                return chain.filter(exchange);
            }
        }
        return ServerWebExchangeUtils.cacheRequestBody(exchange,
                (serverHttpRequest) -> {
                    final Object cachedBodyAttribute = exchange.getAttribute(CACHED_REQUEST_BODY_ATTR);
                    if (cachedBodyAttribute instanceof DataBuffer) {
                        final DataBuffer dataBuffer = (DataBuffer)cachedBodyAttribute;
                        String string = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()).toString();
                        log.info("请求body内容:{}", string);
                        exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, string);
                    }
                    return chain.filter(exchange.mutate().request(serverHttpRequest).build());
                });
//        return DataBufferUtils.join(exchange.getRequest().getBody())
//                .flatMap(dataBuffer -> {
//                    DataBufferUtils.retain(dataBuffer);
//                    Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
//                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
//                        @Override
//                        public Flux<DataBuffer> getBody() {
//                            return cachedFlux;
//                        }
//                    };
//                    // cache body，解决校验参数获取不到body问题
//                    exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux);
//                    ServerWebExchange newExchange = exchange.mutate().request(mutatedRequest).build();
//
//                    return chain.filter(newExchange);
//                });
    }


    @Override
    public int getOrder() {
        return OrderConstants.CACHE_BODY;
    }
}